using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Net;
using Momentum.Static;
using System.Text;

namespace Momentum.Types
{
    public class mObject
    {

        #region Instance Variables

        /// <summary>
        /// Variable con el nombre del objeto
        /// </summary>
        protected string m_Name;

        /// <summary>
        /// Tipo de el mObject.
        /// </summary>
        protected mStaticObject.Types m_Type;

        /*/// <summary>
        /// Subtipo del mObject.
        /// </summary>
        protected int m_Subtype;*/

        /// <summary>
        /// Vector Posicion 2D que ocupa el Objeto (su centro).
        /// </summary>
        protected Vector2 m_Position;

        /// <summary>
        /// Vector Velocidad en 2D actual de el mObject.
        /// </summary>
        protected Vector2 m_Velocity;

        /// <summary>
        /// Puntos de Golpe restantes.
        /// </summary>
        protected int m_HP;

        /// <summary>
        /// Puntos de golpe totales
        /// </summary>
        protected int m_TotalHP;

        /// <summary>
        /// Masa de el Objeto
        /// </summary>
        protected int m_Mass;

        /// <summary>
        /// Radio de colision para choques
        /// </summary>
        protected int m_CollisionRadius;

        /// <summary>
        /// Sprite de el mObject.
        /// </summary>
        protected Texture2D m_ObjectTexture;

        /// <summary>
        /// Sprite de la sombra del mObject
        /// </summary>
        protected Texture2D m_ObjectShadowTexture;

        /// <summary>
        /// Offset del sprite de la sombra con el del objeto
        /// </summary>
        protected Vector2 m_ShadowOffset;

        /// <summary>
        /// Rotacion de el objeto (y su Sprite).
        /// </summary>
        protected float m_ObjectRotation;

        /// <summary>
        /// Escalamiento de el Sprite.
        /// </summary>
        protected float m_ObjectScale;

        /// <summary>
        /// La posicion de la camara. Se utiliza para dibujar adecuadamente el objeto en la pantalla.
        /// </summary>
        protected Vector2 m_CameraPosition;

        #endregion

        #region Getters and Setters

        public string Name
        {
            get { return m_Name; }
            set { m_Name = value; }
        }

        [ContentSerializerIgnore]
        public Vector2 CameraPosition
        {
            get { return m_CameraPosition; }
            set { m_CameraPosition = value; }
        }

        public int CollisionRadius
        {
            get { return m_CollisionRadius; }
            set { m_CollisionRadius = value; }
        }

        [ContentSerializerIgnore]
        public int HP
        {
            get { return m_HP; }
            set { m_HP = value; }
        }

        public int Mass
        {
            get { return m_Mass; }
            set { m_Mass = value; }
        }

        [ContentSerializerIgnore]
        public float ObjectRotation
        {
            get { return m_ObjectRotation; }
            set { m_ObjectRotation = value; }
        }

        public float ObjectScale
        {
            get { return m_ObjectScale; }
            set { m_ObjectScale = value; }
        }

        [ContentSerializerIgnore]
        public Texture2D ObjectTexture
        {
            get { return m_ObjectTexture; }
            set { m_ObjectTexture = value; }
        }

        [ContentSerializerIgnore]
        public Texture2D ObjectShadowTexture
        {
            get { return m_ObjectShadowTexture; }
            set { m_ObjectShadowTexture = value; }
        }

        [ContentSerializer]
        public Vector2 ShadowOffset
        {
            get { return m_ShadowOffset; }
            set { m_ShadowOffset = value; }
        }

        [ContentSerializerIgnore]
        public Vector2 Position
        {
            get { return m_Position; }
            set { m_Position = value; }
        }

        [ContentSerializerIgnore]
        public float PositionX
        {
            get { return m_Position.X; }
            set { m_Position.X = value; }
        }

        [ContentSerializerIgnore]
        public float PositionY
        {
            get { return m_Position.Y; }
            set { m_Position.Y = value; }
        }

        public int TotalHP
        {
            get { return m_TotalHP; }
            set { m_TotalHP = value; m_HP = value; }
        }

        [ContentSerializerIgnore]
        public mStaticObject.Types Type
        {
            get { return m_Type; }
            set { m_Type = value; }
        }

        [ContentSerializerIgnore]
        public Vector2 Velocity
        {
            get { return m_Velocity; }
            set { m_Velocity = value; }
        }

        #endregion

        #region Initialization

        /// <summary>
        /// Default constructor.
        /// </summary>
        public mObject()
        {
            this.Name = "";
            this.CameraPosition = Vector2.Zero;
            this.CollisionRadius = 0;
            this.HP = 0;
            this.Mass = 0;
            this.ObjectRotation = 0.0f;
            this.ObjectScale = 1.0f;
            this.ObjectTexture = null;
            this.Position = Vector2.Zero;
            //this.Subtype = 0;
            this.TotalHP = 0;
            this.Type = mStaticObject.Types.None;
            this.Velocity = Vector2.Zero;

        }

        #endregion

        #region Momentum

        public void Move(float friction)
        {
            Velocity = Velocity - friction*Velocity;
            Position = Position + Velocity;
        }

        /// <summary>
        /// Updates the positions and velocities of colliding objects.
        /// </summary>
        /// <param name="o1">The first object</param>
        /// <param name="o2">The second object</param>
        public static void Collide(mObject o1, mObject o2)
        {
            if (!Colliding(o1, o2)) return;
            Separate(o1, o2);
            o1.HP -= Damage(o1, o2);
            o2.HP -= Damage(o1, o2);
            Vector2 Line = o2.Position - o1.Position;
            float phi = (float)Math.Atan2(Line.Y, Line.X);
            float phi_o1 = (float)Math.Atan2(o1.Velocity.Y, o1.Velocity.X);
            float phi_o2 = (float)Math.Atan2(o2.Velocity.Y, o2.Velocity.X);
            Vector2 vel_o1 = new Vector2((float)(o1.Velocity.Length() * Math.Cos(phi_o1 - phi)), (float)(o1.Velocity.Length() * Math.Sin(phi_o1 - phi)));
            Vector2 vel_o2 = new Vector2((float)(o2.Velocity.Length() * Math.Cos(phi_o2 - phi)), (float)(o2.Velocity.Length() * Math.Sin(phi_o2 - phi)));
            float new_vel_o1 = ((o1.Mass - o2.Mass) * vel_o1.X + 2 * o2.Mass * vel_o2.X) / (o1.Mass + o2.Mass);
            float new_vel_o2 = ((o2.Mass - o1.Mass) * vel_o2.X + 2 * o1.Mass * vel_o1.X) / (o1.Mass + o2.Mass);
            o1.Velocity = new Vector2((float)(Math.Cos(phi) * new_vel_o1 + Math.Cos(phi + Math.PI / 2) * vel_o1.Y),
                (float)(Math.Sin(phi) * new_vel_o1 + Math.Sin(phi + Math.PI / 2) * vel_o1.Y));
            o2.Velocity = new Vector2((float)(Math.Cos(phi) * new_vel_o2 + Math.Cos(phi + Math.PI / 2) * vel_o2.Y),
                (float)(Math.Sin(phi) * new_vel_o2 + Math.Sin(phi + Math.PI / 2) * vel_o2.Y));
        }

        /// <summary>
        /// Returns true if two objects are colliding.
        /// </summary>
        /// <param name="o1">The first object</param>
        /// <param name="o2">The second object</param>
        /// <returns>bool</returns>
        public static bool Colliding(mObject o1, mObject o2)
        {
            return (Vector2.Distance(o1.Position, o2.Position) < o1.CollisionRadius + o2.CollisionRadius);
        }

        /// <summary>
        /// Returns the damage based on two colliding objects.
        /// </summary>
        /// <param name="o1">The first object</param>
        /// <param name="o2">The second object</param>
        /// <returns>int</returns>
        public static int Damage(mObject o1, mObject o2)
        {
            return (int)(o1.Mass * (int)o1.Velocity.Length() + o2.Mass * (int)o2.Velocity.Length()) / 10000;
        }

        /// <summary>
        /// Separates two objects that are colliding.
        /// </summary>
        /// <param name="o1">The first object</param>
        /// <param name="o2">The second object</param>
        public static void Separate(mObject o1, mObject o2)
        {
            Vector2 Distance = o1.Position - o2.Position;
            float difference = o1.CollisionRadius + o2.CollisionRadius - Distance.Length();
            Distance.Normalize();
            o1.Position += Distance * (int)Math.Ceiling(difference / 2);
            o2.Position -= Distance * (int)Math.Ceiling(difference / 2);
        }

        #endregion

        #region Draw and Update

        public virtual void Draw(SpriteBatch spriteBatch, Color overlay)
        {
            Vector2 origen = new Vector2(m_ObjectTexture.Width / 2, m_ObjectTexture.Height / 2);
            Vector2 escala = new Vector2(m_ObjectScale, m_ObjectScale);
            Vector2 pos = m_Position - m_CameraPosition + mStaticWorld.ScreenCenter;
            spriteBatch.Draw(m_ObjectShadowTexture, pos + m_ShadowOffset, null, Color.White, m_ObjectRotation, origen, escala, SpriteEffects.None, 0);
            spriteBatch.Draw(m_ObjectTexture, pos, null, overlay, m_ObjectRotation, origen, escala, SpriteEffects.None, 0);
        }

        public virtual void Draw(SpriteBatch spriteBatch)
        {
            Draw(spriteBatch, Color.White);
        }

        public virtual void UpdatePosition()
        {
            m_Position.X %= mStaticWorld.Size.X;
            if (m_Position.X < 0)
            {
                m_Position.X += mStaticWorld.Size.X;
            }
            m_Position.Y %= mStaticWorld.Size.Y;
            if (m_Position.Y < 0)
            {
                m_Position.Y += mStaticWorld.Size.Y;
            }
        }

        public void SetPositionToCircularWorld()
        {
            SetPositionToCircularWorld(ref m_Position, m_CollisionRadius, m_CameraPosition);
        }

        public static void SetPositionToCircularWorld(ref Vector2 position, int collisionRadius, Vector2 cameraPosition)
        {
            Vector2 border = cameraPosition - mStaticWorld.ScreenCenter;
            if (border.X < collisionRadius && position.X >= mStaticWorld.Size.X + border.X - collisionRadius)
            {
                position.X -= mStaticWorld.Size.X;
            }
            if (border.Y < collisionRadius && position.Y >= mStaticWorld.Size.Y + border.Y - collisionRadius)
            {
                position.Y -= mStaticWorld.Size.Y;
            }
            if (border.X + mStaticWorld.ScreenSize.X >= mStaticWorld.Size.X && position.X <= mStaticWorld.ScreenSize.X + collisionRadius)
            {
                position.X += mStaticWorld.Size.X;
            }
            if (border.Y + mStaticWorld.ScreenSize.Y >= mStaticWorld.Size.Y && position.Y <= mStaticWorld.ScreenSize.Y + collisionRadius)
            {
                position.Y += mStaticWorld.Size.Y;
            }
        }

        #endregion

        #region Serialization

        /// <summary>
        /// Serialize the object out to a packet without the heading.
        /// </summary>
        /// <param name="packetWriter">The packet to write to.</param>
        public virtual void SerializeWH(PacketWriter packetWriter)
        {
            // safety-check the parameters, as they must be valid
            if (packetWriter == null)
            {
                throw new ArgumentNullException("packetWriter");
            }
            packetWriter.Write(Name);
            packetWriter.Write(Position);
            packetWriter.Write(Velocity);
            packetWriter.Write(HP);
            packetWriter.Write(ObjectRotation);
        }

        /// <summary>
        /// Contrario a serializar
        /// </summary>
        /// <param name="packetReader">la wea</param>
        public virtual void Deserialize(PacketReader packetReader)
        {
            // safety-check the parameters, as they must be valid
            if (packetReader == null)
            {
                throw new ArgumentNullException("packetReader");
            }
            Name = packetReader.ReadString();
            Position = packetReader.ReadVector2();
            Velocity = packetReader.ReadVector2();
            HP = packetReader.ReadInt32();
            ObjectRotation = packetReader.ReadSingle();
        }

        #endregion

    }

}